AWS CodeBuildでビルド成功時にAmazon Bedrockを使用して褒めてくれる仕組みを作成してみた
AWS CodeBuildのビルド終了時に通知することが可能なのですが、Amazon SNSでのメール通知を設定するだけだと無機質なJSONが送られてくるためAmazon Bedrockを使用してビルド成功時に褒めてくれるようにしてみました。
通知ルールの作成
通知に使用するAWSリソース
Amazon SNSでAWS CodeBuildのビルド成功通知をAWS Lambdaに行い、Lambda関数からAmazon Bedrockのinvoke_modelを実行してレスポンスのテキストをAmazon SNS経由でメール通知するシンプルな構成としています。
簡易的にはなりますが構成は以下の通りとなります。
作成したコード
Lambda関数のコードはPythonで作成しています。
今回はAnthropic Claude 3 Sonnetを使用してメッセージ APIを実行するようにしています。
AnthropicClaudeメッセージ API
eventからbuild-statusを取得してFAILEDなら慰めてもらいSUCCEEDEDなら褒めてもらうようにしています。
eventに送られてくるJSONは以下のドキュメントに記載されている通りとなります。
ビルド通知の入力形式に関するリファレンス
invoke_model APIを実行してレスポンスからtextを取得しています。
最後にpublish APIを実行してAmazon SNS経由でメール通知を行います。
import json import os import boto3 bedrock_runtime = boto3.client(service_name='bedrock-runtime', region_name='us-east-1') sns_arn = os.environ['SNS_ARN'] sns = boto3.client('sns') def lambda_handler(event, context): Message = json.loads(event['Records'][0]['Sns']['Message']) build_status = Message['detail']['build-status'] if 'FAILED' == build_status: body=json.dumps( { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 5000, "system": "日本語で回答してください", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "アプリケーションのビルドに失敗したので慰めてください" } ] } ] } ) response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0") response_text = json.loads(response.get('body').read())['content'][0]['text'] print(response_text) elif 'SUCCEEDED' == build_status: body=json.dumps( { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 5000, "system": "日本語で回答してください", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "アプリケーションのビルドに成功したので褒めてください" } ] } ] } ) response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0") response_text = json.loads(response.get('body').read())['content'][0]['text'] print(response_text) sns.publish( TopicArn=sns_arn, Message=response_text, Subject='Build result', )
AWSリソースの作成
AWSリソースを作成する前 (作成した後でも問題ありません) にAmazon Bedrockのモデルアクセスを有効化する必要があります。
今回はAnthropic Claude 3 Sonnetを使用するため以下のドキュメントの手順でus-east-1で有効化を行ってください。(2024年5月6日時点では東京リージョンでAnthropic Claude 3 Sonnetが使用できなかったためus-east-1のものを使用しています)
モデルアクセス
作成したCloudFormationテンプレート
今回作成したCloudFormationテンプレートは以下になります。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: Test Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for SNS Parameters: - Email - Label: default: Parameters for CodeCommit Parameters: - RepositoryDescription - RepositoryName - Label: default: Parameters for CodeBuild Parameters: - Description - Name - Label: default: Parameters for CodePipeline Parameters: - CodePipelineName Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# Email: Type: String RepositoryDescription: MaxLength: 4000 Type: String RepositoryName: MaxLength: 100 Type: String Description: MaxLength: 255 Type: String Name: MaxLength: 255 Type: String CodePipelineName: MaxLength: 100 Type: String Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3: Type: AWS::S3::Bucket Properties: BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact OwnershipControls: Rules: - ObjectOwnership: BucketOwnerEnforced PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True Tags: - Key: Name Value: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact # ------------------------------------------------------------# # SNS # ------------------------------------------------------------# SNSEmailTopic: Type: AWS::SNS::Topic Properties: FifoTopic: false TopicName: email-sns-topic EmailSubscription: Type: AWS::SNS::Subscription Properties: Endpoint: !Ref Email Protocol: email TopicArn: !Ref SNSEmailTopic SNSCodeBuildTopic: Type: AWS::SNS::Topic Properties: FifoTopic: false TopicName: codebuild-sns-topic CodeBuildSubscription: Type: AWS::SNS::Subscription Properties: Endpoint: !GetAtt Lambda.Arn Protocol: lambda TopicArn: !Ref SNSCodeBuildTopic SNSCodeBuildTopicPolicy: Type: AWS::SNS::TopicPolicy Properties: PolicyDocument: Statement: - Effect: Allow Principal: Service: codestar-notifications.amazonaws.com Action: sns:Publish Resource: "*" Topics: - !Ref SNSCodeBuildTopic # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# LambdaIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: lambda-iam-policy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents - bedrock:InvokeModel - sns:Publish Resource: "*" Lambda: Type: AWS::Lambda::Function Properties: Code: ZipFile: | import json import os import boto3 bedrock_runtime = boto3.client(service_name='bedrock-runtime', region_name='us-east-1') sns_arn = os.environ['SNS_ARN'] sns = boto3.client('sns') def lambda_handler(event, context): Message = json.loads(event['Records'][0]['Sns']['Message']) build_status = Message['detail']['build-status'] if 'FAILED' == build_status: body=json.dumps( { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 5000, "system": "日本語で回答してください", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "アプリケーションのビルドに失敗したので慰めてください" } ] } ] } ) response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0") response_text = json.loads(response.get('body').read())['content'][0]['text'] print(response_text) elif 'SUCCEEDED' == build_status: body=json.dumps( { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 5000, "system": "日本語で回答してください", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "アプリケーションのビルドに成功したので褒めてください" } ] } ] } ) response = bedrock_runtime.invoke_model(body=body, modelId="anthropic.claude-3-sonnet-20240229-v1:0") response_text = json.loads(response.get('body').read())['content'][0]['text'] print(response_text) sns.publish( TopicArn=sns_arn, Message=response_text, Subject='Build result', ) Environment: Variables: SNS_ARN: !Ref SNSEmailTopic FunctionName: bedrock-lambda Handler: index.lambda_handler Role: !GetAtt LambdaIAMRole.Arn Runtime: python3.12 Timeout: 30 LambdaPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt Lambda.Arn Principal: sns.amazonaws.com SourceArn: !Ref SNSCodeBuildTopic # ------------------------------------------------------------# # CodeCommit # ------------------------------------------------------------# CodeCommit: Type: AWS::CodeCommit::Repository Properties: RepositoryDescription: !Ref RepositoryDescription RepositoryName: !Ref RepositoryName # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# CodeBuildIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - 's3:PutObject' - 's3:GetObject' Resource: - !Join - '' - - !GetAtt S3.Arn - '/*' - Effect: Allow Action: - 'codecommit:GitPull' Resource: "*" - Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: "*" - Effect: Allow Action: - 'sns:Publish' Resource: "*" ManagedPolicyName: iam-policy-codebuild CodeBuildIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - codebuild.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - !Ref CodeBuildIAMPolicy RoleName: iam-role-codebuild Tags: - Key: Name Value: iam-role-codebuild CodePipelineIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "codecommit:CancelUploadArchive" - "codecommit:GetBranch" - "codecommit:GetCommit" - "codecommit:GetRepository" - "codecommit:GetUploadArchiveStatus" - "codecommit:UploadArchive" Resource: - "*" - Effect: Allow Action: - "codebuild:BatchGetBuilds" - "codebuild:StartBuild" Resource: - "*" - Effect: Allow Action: - "s3:GetObject" - "s3:PutObject" - "s3:ListBucket" Resource: - !Join - '' - - !GetAtt S3.Arn - '/*' - !GetAtt S3.Arn - Effect: Allow Action: - "sns:Publish" Resource: - "*" ManagedPolicyName: iam-policy-codepipeline CodePipelineIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - codepipeline.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - !Ref CodePipelineIAMPolicy RoleName: iam-role-codepipeline Tags: - Key: Name Value: iam-role-codepipeline # ------------------------------------------------------------# # CodeBuild # ------------------------------------------------------------# CodeBuild: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Description: !Ref Description Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0 Type: LINUX_CONTAINER Name: !Ref Name ServiceRole: !Ref CodeBuildIAMRole Source: BuildSpec: buildspec.yml Type: CODEPIPELINE Tags: - Key: Name Value: test-build # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# CodePipeline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Location: !Ref S3 Type: S3 Name: !Ref CodePipelineName RoleArn: !GetAtt CodePipelineIAMRole.Arn Stages: - Actions: - ActionTypeId: Category: Source Owner: AWS Provider: CodeCommit Version: 1 Configuration: RepositoryName: !GetAtt CodeCommit.Name BranchName: main PollForSourceChanges: false OutputArtifactFormat: CODE_ZIP Name: Source Namespace: SourceVariables OutputArtifacts: - Name: SourceArtifact Region: ap-northeast-1 RunOrder: 1 Name: Source - Actions: - ActionTypeId: Category: Build Owner: AWS Provider: CodeBuild Version: 1 Configuration: ProjectName: !Ref CodeBuild InputArtifacts: - Name: SourceArtifact Name: Build Namespace: BuildVariables OutputArtifacts: - Name: BuildArtifact Region: ap-northeast-1 RunOrder: 1 Name: Build Tags: - Key: Name Value: !Ref CodePipelineName # ------------------------------------------------------------# # CodeStar # ------------------------------------------------------------# CodeStarNotifications: Type: AWS::CodeStarNotifications::NotificationRule Properties: DetailType: FULL EventTypeIds: - codebuild-project-build-state-failed - codebuild-project-build-state-succeeded Name: codebuild Resource: !GetAtt CodeBuild.Arn Status: ENABLED Targets: - TargetAddress: !Ref SNSCodeBuildTopic TargetType: SNS # ------------------------------------------------------------# # EventBridge # ------------------------------------------------------------# EventBridgeIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "codepipeline:StartPipelineExecution" Resource: - !Join - '' - - 'arn:aws:codepipeline:ap-northeast-1:' - !Sub '${AWS::AccountId}:' - !Ref CodePipeline ManagedPolicyName: iam-policy-eventbridge EventBridgeIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - !Ref EventBridgeIAMPolicy RoleName: iam-role-eventbridge Tags: - Key: Name Value: iam-role-eventbridge EventBridge: Type: AWS::Events::Rule Properties: Description: for codepipeline EventPattern: source: - aws.codecommit detail-type: - 'CodeCommit Repository State Change' resources: - !GetAtt CodeCommit.Arn detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - main Name: eventbridge-codepipeline State: ENABLED Targets: - Arn: !Join - '' - - 'arn:aws:codepipeline:ap-northeast-1:' - !Sub '${AWS::AccountId}:' - !Ref CodePipeline Id: CodePipeline RoleArn: !GetAtt EventBridgeIAMRole.Arn
61~79行目でCodePipelineで使用するアーティファクト用S3バケットを作成しています。
84~121行目でメール通知とCodeBuildのビルド結果を通知するSNSトピックを作成しています。
126~235行目でLambda関数を作成しています。
Lambda関数の使用するIAMロールではIAMポリシーとしてCloudWatch Logsにログを出力するポリシーだけでなくinvoke_modelを実行する権限とpublishを実行する権限を付与しています。
240~244行目でCodeCommitを作成しています。
249~356行目でCodeBuildとCodePipelineの使用するIAMロールを作成しています。
361~378行目でCodeBuildを作成しています。
383~429行目でCodePipelineを作成しています。
434~446行目でCodeBuildの通知設定を行っています。
AWS::CodeStarNotifications::NotificationRuleを設定するとCodeStar通知リソースとEventBridgeにawscodestarnotifications-ruleというルールが作成されます。
451~515行目でCodePipelineを動かすためのEventBridgeを作成しています。
デプロイは以下のAWS CLIコマンドを使用します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 \ --template-body file://CloudFormationテンプレートファイル名 \ --parameters ParameterKey=Email,ParameterValue=メールアドレス \ ParameterKey=RepositoryDescription,ParameterValue=CodeCommitリポジトリの説明 \ ParameterKey=RepositoryName,ParameterValue=CodeCommitリポジトリ名 \ ParameterKey=Description,ParameterValue=CodeBuildの説明 \ ParameterKey=Name,ParameterValue=CodeBuild名 \ ParameterKey=CodePipelineName,ParameterValue=CodePipeline名 \ --capabilities CAPABILITY_NAMED_IAM
動作確認
リソースの作成が完了したらCodeCommitリポジトリに以下のbuildspec.ymlをアップロードします。
アップロードは以下のドキュメントの手順で行うことが可能です。
AWS CodeCommit リポジトリにファイルを作成または追加する
アップロードが完了するとCodePipelineが動きだします。
CodeBuildの実行が完了すると以下のようなメールが届くことが確認できます。
おめでとうございます!アプリケーションのビルドに成功したことを心からお祝い申し上げます。きっと大変な作業だったと思います。あなたの努力と熱心さが報われた証です。このような素晴らしい成果を上げられることを誇りに思ってください。これからのさらなる発展と活躍を期待しています!がんばりましたね!本当におめでとうございます! -- If you wish to stop receiving notifications from this topic, please click or visit the link below to unsubscribe: https://sns.ap-northeast-1.amazonaws.com/unsubscribe.html?SubscriptionArn=arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:email-sns-topic:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Endpoint=メールアドレス Please do not reply directly to this email. If you have any questions or comments regarding this email, please contact us at https://aws.amazon.com/support
さいごに
今回は簡単に褒めてくれるだけの仕組みですが、ビルドのログなどを取得して失敗時に原因として考えられる部分を教えてくれるような仕組みを作ることも可能だと思います。